/*
 * @Author: your name
 * @Date: 2021-08-16 08:36:35
 * @LastEditTime: 2022-04-04 11:03:54
 * @LastEditors: Please set LastEditors
 * @Description: 输入法界面：切换输入法、输入法候选字功能。多态单例。
 * 使用输入法类方法：
 * 1、派生类继承该类并重载该类虚函数；
 * 2、派生类构造时，必须调用SetInstance()函数初始化单例，析构时，调用SetInstance(nullptr)；
 * 3、派生类SetEdit()设置相关参数；
 * @FilePath: \phone\src\input_method.h
 */
#ifndef INPUT_METHOD_H
#define INPUT_METHOD_H
#include "common.h"
#include "gui_engine.h"
#include <map>
#include <mutex>
/**
 * @brief
 * 编辑框值的view控件名，派生类必须使用该宏设置编辑框的属性
 */
#define EDIT_VALUE "editValue"
#define STOP_SHOW_TIME 1000
enum class InputMethodsType {
    INPUT_METHOD_NUM = 0,
    INPUT_METHOD_LOWERCASE,
    INPUT_METHOD_MAJUSCULE,
    INPUT_METHOD_ALL_ABOVE,
    FUNCTION_END
};
class InputMethod {
public:
    static InputMethod* GetInstance()
    {
        return instance;
    }
    static void SetInstance(InputMethod* p)
    {
        if (p) {
            p->Init();
        }
        instance = p;
    }
    // InputMethod();
    InputMethod(std::unique_ptr<Widget>& page);
    virtual ~InputMethod();
    void Init();
    void InputOperation(void* ctx);
    void InputOperation(KeyboardValue key);
    void ChooseShow(KeyboardValue key);
    void ChooseShowStop(std::string target);
    void ChooseShowStopIQ(std::string target);
    bool EditRefresh(KeyboardValue key);
    void EditShowValue();
    void EditRefreshIQ(std::string value);
    void ChooseShowIQ(KeyboardValue key);
    bool SwitchSelectedItem(KeyboardValue key);
    void CreateChooseView(KeyboardValue key);
    void SetItemView(Widget* view);
    bool CreateItemView(Widget* target, Widget* view2);
    void CreateOneItem(Widget* view2, int iTarget, KeyboardValue key);
    std::string GetOneChooseCharacter(unsigned int target, KeyboardValue key);
    unsigned int GetChooseLength(KeyboardValue key);
    void MoveCursor(void* ctx);
    void MoveCursorIQ();
    void Delete(void* ctx);
    void InputMethodChange();
    void ChooseInputMethod(InputMethodsType method);
    void ChooseInputMethodIQ(InputMethodsType method);
    bool SwitchSelectedItem(InputMethodsType method);
    void CreateChooseView(InputMethodsType method);
    void CreateOneItem(Widget* view2, int iTarget, InputMethodsType method);
    /**
     * @brief Set the Edit Text object
     * 设置编辑框的值，抽象方法，由继承类实现，适配不同界面编辑框显示不同属性的值（一般是字符大小）。
     * 派生类在设置编辑框的text时，要统一使用label控件创建字符集，且保证label控件宽度一致，方便光标操作。
     * @param edit 当前编辑框
     * @param ctx 当前编辑框上下文
     */
    virtual void SetEditText(Widget* edit, std::string value, void* ctx) { }
    /**
     * @brief
     * 抽象的输入法切换函数，默认切换轮流切换所有输入法，派生类可重载实现切换算法。
     * @param method
     */
    virtual void InputMethodSelection(InputMethodsType& method, InputMethodsType& lastMethod)
    {
        lastMethod = method;
        CommonFunc::GetInstance().FunctionSelection(method, KeyboardValue::KEY_DIRECTION_RIGHT);
    }
    /**
     * @brief 
     * 输入完成事件，字符可选时在可选字符串消失后才触发，派生类根据实际需要捕获事件。
     */
    virtual void InputFinshedEvent() { }
    void InputFinshedEventIQ();
    /**
     * @brief Set the Edit object
     * 设置输入法当前编辑框
     * @param edit 当前编辑框
     * @param cursorWidth 编辑框单个字符宽度
     * @param ctx 编辑框上下文
     * @param editLength 编辑框最大字符长度
     * @param editValue 编辑框初始值
     */
    void SetEdit(Widget* edit, unsigned int cursorWidth, void* ctx, unsigned int editLength, std::string editValue);
    unsigned int NextIndex(KeyboardValue key);
    /**
     * @brief 
     * 检查字符长度，检查一串数据中有多少个字符，比如中文3个字节对一字符
     * @param key 
     * @return ************ int 
     */
    int GetCharLength(unsigned char *databuffer,int buffer_zie);
    /**
     * @brief Get the Char Index object
     * 获取第n个字符起始下标
     * @param databuffer 
     * @param buffer_zie 
     * @param char_index 
     * @return ** int 
     */
    int GetNCharIndex(unsigned char *databuffer,int buffer_zie,int n);

    /**
     * @brief 
     * 获取第n个字符
     * @param databuffer 收缩的字符串
     * @param char_out 输出字符串
     * @param psize 输出长度
     * @param n 第几个字符
     * @return ** int 
     */
    int GetNChar(unsigned char *databuffer,unsigned char *char_out,int *psize,int n);

    void SetInputMethodType(InputMethodsType type);
    virtual void ShowCursor();
    std::string GetEditValue()
    {
        return editValue;
    }
    unsigned int GetStartIndex()
    {
        return startIndex;
    }
    unsigned int GetCursor()
    {
        return cursor;
    }
    void SetMaxLength(unsigned int max)
    {
        maxLength = max;
    }
    bool& GetChoosing();

private:
    /**
     * @brief
     * 输入法多态实例，不同的界面、场景需要派生不同的类，派生类实例化的时候，实例该参数。
     * 只保存，不管理
     */
    static InputMethod* instance;
    /**
     * @brief
     * 输入法候选字符
     */
    std::map<KeyboardValue, std::string> choose[static_cast<int>(InputMethodsType::FUNCTION_END)];
    /**
     * @brief
     * 输入法类型
     */
    InputMethodsType inputMethod;
    /**
     * @brief
     * 主要的窗口，该类依赖一个窗口，控制主窗口的某个区域。
     */
    std::unique_ptr<Widget>& primaryWidget;
    /**
     * @brief
     * 输入法名称译文
     */
    std::string IM_TRTEXT[static_cast<int>(InputMethodsType::FUNCTION_END)];
    /**
     * @brief
     * 输入法操作的edit控件，由主窗口设置，输入法填数据。
     */
    std::unique_ptr<Widget> edit;
    /**
     * @brief
     * 编辑框上下文
     */
    void* ctx;
    /**
     * @brief
     * 光标移动的单位宽度，由派生类初始化
     */
    unsigned int cursorWidth;
    /**
     * @brief
     * 候选字符显示区域
     */
    // std::unique_ptr<Widget> chooseView;
    /**
     * @brief
     * 输入法切换显示区域
     */
    // std::unique_ptr<Widget> inputMethodView;
    /**
     * @brief
     * 上次按下的按键键值
     */
    KeyboardValue lastKey;
    /**
     * @brief
     * 编辑窗口缓存的密码值
     */
    std::string editValue;
    /**
     * @brief
     * 当前候选字符的下标
     */
    unsigned int index;
    /**
     * @brief
     * 字符显示线程运行标识
     */
    bool chooseRuning;
    /**
     * @brief
     * 输入法切换显示线程运行标识
     */
    bool IMRuning;
    /**
     * @brief
     * 输入框的光标坐标
     */
    unsigned int cursor;
    /**
     * @brief
     * 候选字符持续显示时间，如果案件快速连续按下一次，持续时间重置一次
     */
    unsigned int showTime;
    /**
     * @brief
     * 编辑框的最大长度
     */
    unsigned int editLength;
    /**
     * @brief
     * 输入值显示的起始下标，当输入字符串大于编辑框的最大长度时，只显示部分字符串。
     */
    unsigned int startIndex;
    /**
     * @brief
     * 输入字符的最大长度，默认为0，表示无限制。
     */
    unsigned int maxLength;
    /**
     * @brief
     * 输入框起始坐标，基于edit。默认52(适配所有的设置界面输入设置项)。
     */
    // const unsigned int editX;
    std::mutex m_mutex;
};
#endif